#include "simulationunit.h"

#include <stdio.h>


SimulationUnit::SimulationUnit(LPCTSTR szFmuFileName, fmi2String instanceName, fmi2String resourcePath)
{
    m_hModule = LoadLibrary(szFmuFileName);
    if (!m_hModule)
    {
        m_errorCode = GetLastError();
        return;
    }

    m_fpGetTypesPlatform = (fmi2GetTypesPlatformTYPE *)GetProcAddress(m_hModule, "fmi2GetTypesPlatform");
    m_fpGetVersion = (fmi2GetVersionTYPE *)GetProcAddress(m_hModule, "fmi2GetVersion");

    m_fpSetupExperiment = (fmi2SetupExperimentTYPE *)GetProcAddress(m_hModule, "fmi2SetupExperiment");

    m_fpGetInteger = (fmi2GetIntegerTYPE *)GetProcAddress(m_hModule, "fmi2GetInteger");
    m_fpSetInteger = (fmi2SetIntegerTYPE *)GetProcAddress(m_hModule, "fmi2SetInteger");
    
    m_fpInstantiate = (fmi2InstantiateTYPE *)GetProcAddress(m_hModule, "fmi2Instantiate");
    m_fpFreeInstance = (fmi2FreeInstanceTYPE *)GetProcAddress(m_hModule, "fmi2FreeInstance");
    m_fpDoStep = (fmi2DoStepTYPE *)GetProcAddress(m_hModule, "fmi2DoStep");

    if (!instantiate(instanceName))
    {
        FreeLibrary(m_hModule);
        m_hModule = nullptr;
        m_errorCode = 9999;
        return;
    }
}

SimulationUnit::~SimulationUnit()
{
    freeInstance();
    if (m_hModule)
    {
        FreeLibrary(m_hModule);
        m_hModule = nullptr;
    }
}

bool SimulationUnit::instantiate(fmi2String instanceName, fmi2String resourcePath)
{
    if (!m_fpInstantiate)
    {
        return false;
    }
    m_pUnitInstance = m_fpInstantiate(instanceName, fmi2CoSimulation, "{00000000-0000-0000-0000-000000000000}", resourcePath, nullptr, fmi2False, fmi2False);
    return (m_pUnitInstance != nullptr);
}

void SimulationUnit::freeInstance()
{
    if (!m_fpFreeInstance || !m_pUnitInstance)
    {
        return;
    }
    m_fpFreeInstance(m_pUnitInstance);
}

const char *SimulationUnit::getTypesPlatform() const
{
    if (!m_fpGetTypesPlatform)
    {
        return "unknown";
    }
    return m_fpGetTypesPlatform();
}

const char *SimulationUnit::getVersion() const
{
    if (!m_fpGetVersion)
    {
        return "unknown";
    }
    return m_fpGetVersion();
}

fmi2Status SimulationUnit::setupExperiment(fmi2Real timeStart, fmi2Real timeStop)
{
    if (!m_fpSetupExperiment || !m_pUnitInstance)
    {
        return fmi2Fatal;
    }
    return m_fpSetupExperiment(m_pUnitInstance, fmi2False, 0.0, timeStart, fmi2True, timeStop);
}

fmi2Status SimulationUnit::getInteger(const fmi2ValueReference vr[], size_t nvr, fmi2Integer value[])
{
    if (!m_fpGetInteger || !m_pUnitInstance)
    {
        return fmi2Fatal;
    }
    return m_fpGetInteger(m_pUnitInstance, vr, nvr, value);
}

fmi2Status SimulationUnit::setInteger(const fmi2ValueReference vr[], size_t nvr, const fmi2Integer value[])
{
    if (!m_fpSetInteger || !m_pUnitInstance)
    {
        return fmi2Fatal;
    }
    return m_fpSetInteger(m_pUnitInstance, vr, nvr, value);
}

fmi2Status SimulationUnit::doStep(fmi2Real timeCurrent, fmi2Real timeInterval)
{
    if (!m_fpDoStep || !m_pUnitInstance)
    {
        return fmi2Fatal;
    }
    if (timeInterval <= 0.0)
    {
        return fmi2Error;
    }
    return m_fpDoStep(m_pUnitInstance, timeCurrent, timeInterval, fmi2False);
}